package com.fintech.pangu.elasticsearch.service.impl;

import com.fintech.pangu.elasticsearch.service.IndexService;
import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexResponse;
import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsRequest;
import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsResponse;
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingResponse;
import org.elasticsearch.client.IndicesAdminClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;

/**
 * 索引操作服务
 *
 * @author xujunqi
 * @date 2019/1/21 13:54
 */
public class IndexServiceImpl implements IndexService {
    private final static Logger logger = LoggerFactory.getLogger(IndexServiceImpl.class);

    private IndicesAdminClient indicesAdminClient;

    private static final String DEFAULT_MAX_RESULT_WINDOWSIZE = "10000000";

    /**
     * 索引的max_result_window参数
     */
    private String maxResultWindowSize = DEFAULT_MAX_RESULT_WINDOWSIZE;

    public IndexServiceImpl() {
    }

    public IndexServiceImpl(IndicesAdminClient indicesAdminClient) {
        this.indicesAdminClient = indicesAdminClient;
    }

    public IndexServiceImpl indicesAdminClient(IndicesAdminClient indicesAdminClient) {
        this.indicesAdminClient = indicesAdminClient;
        return this;
    }

    public IndexServiceImpl maxResultWindowSize(String maxResultWindowSize) {
        this.maxResultWindowSize = maxResultWindowSize;
        return this;
    }

    /**
     * 创建索引
     *
     * @param index 索引名称
     * @return Boolean 是否创建成功
     */
    @Override
    public Boolean createIndex(String index) throws Exception {
        Assert.notNull(index, "索引名称为空");

        if (indexNameInvalid(index)) {
            throw new IllegalArgumentException("索引名称必须是小写");
        }

        if (indexExists(index)) {
            throw new Exception("索引名称已经存在");
        }

        logger.debug("申请创建索引[index={}]", index);
        CreateIndexResponse response = indicesAdminClient.prepareCreate(index).setSettings(Settings.builder()
                .put("index.max_result_window", maxResultWindowSize)
                .build()).get();
        logger.debug("索引[index={}]创建操作完成", index);

        Boolean ack = response.isAcknowledged();
        if (ack) {
            logger.debug("索引[index={}]创建成功", index);
        }

        return ack;
    }

    /**
     * 创建索引, 默认索引名称与文档类型名称相同
     *
     * @param index
     * @param jsonMapping
     * @return
     */
    @Override
    public Boolean createIndex(String index, String jsonMapping) throws Exception {
        return createIndex(index, index, jsonMapping, XContentType.JSON);
    }

    /**
     * 创建索引
     *
     * @param index       索引名称
     * @param type        文档类型名称
     * @param jsonMapping mapping映射定义信息(Json格式)
     * @return Boolean 是否创建成功
     */
    @Override
    public Boolean createIndex(String index, String type, String jsonMapping) throws Exception {
        return createIndex(index, type, jsonMapping, XContentType.JSON);
    }


    /**
     * 创建索引
     *
     * @param index        索引名称
     * @param type         文档类型名称
     * @param jsonMapping  mapping映射定义信息
     * @param xContentType mapping映射定义格式类型
     * @return
     */
    private Boolean createIndex(String index, String type, String jsonMapping, XContentType xContentType) throws Exception {
        Assert.notNull(index, "索引名称为空");
        Assert.notNull(type, "文档类型名称为空");
        Assert.notNull(jsonMapping, "文档mapping为空");

        if (indexNameInvalid(index)) {
            throw new IllegalArgumentException("索引名称必须是小写");
        }

        if (indexExists(index)) {
            throw new Exception("索引名称已经存在");
        }

        logger.debug("申请创建索引[index={}, type={}, jsonMapping={}]", index, type, jsonMapping);
        CreateIndexResponse response = indicesAdminClient.prepareCreate(index).addMapping(type, jsonMapping, xContentType).get();
        logger.debug("索引[index={}, type={}]创建操作完成", index, type);

        Boolean ack = response.isAcknowledged();
        if (ack) {
            logger.debug("索引[index={}, type={}]创建成功", index, type);
        }

        return response.isAcknowledged();
    }

    /**
     * 删除指定的索引
     *
     * @param index
     * @return
     */
    @Override
    public Boolean deleteIndex(String index) throws Exception {
        Assert.notNull(index, "索引名称为空");

        if (!indexExists(index)) {
            throw new Exception("索引名称不存在");
        }

        logger.debug("申请删除索引[index={}]", index);
        DeleteIndexResponse response = indicesAdminClient.prepareDelete(index).get();
        logger.debug("索引[index={}]删除操作完成", index);

        Boolean ack = response.isAcknowledged();
        if (ack) {
            logger.debug("索引[index={}]删除成功", index);
        }

        return ack;
    }

    /**
     * 更改已经索引的mapping映射
     *
     * @param index
     * @param type
     * @param jsonMapping
     * @return
     */
    @Override
    public Boolean putMapping(String index, String type, String jsonMapping) throws Exception {
        return putMapping(index, type, jsonMapping, XContentType.JSON);
    }

    /**
     * 更改已经索引的mapping映射
     *
     * @param index
     * @param type
     * @param jsonMapping
     * @param xContentType
     * @return
     */
    private Boolean putMapping(String index, String type, String jsonMapping, XContentType xContentType) throws Exception {
        if (!indexExists(index)) {
            logger.warn("申请更新索引[index={}]的mapping映射, 索引不存在", index);
            throw new Exception("索引不存在");
        }

        logger.debug("申请更新索引[index={}, type={}]mapping映射", index, type);
        PutMappingResponse response = indicesAdminClient.preparePutMapping(index).setType(type).setSource(jsonMapping, xContentType).get();
        logger.debug("索引[index={}, type={}]mapping映射更新完成", index, type);

        Boolean ack = response.isAcknowledged();
        if (ack) {
            logger.debug("索引[index={}, type={}]mapping映射更新成功", index, type);
        }

        return ack;
    }

    /**
     * 判断索引
     *
     * @param index
     * @return
     */
    private Boolean indexNameValid(String index) {
        Assert.notNull(index, "索引名称为空");

        return index.equals(index.toLowerCase());
    }

    private Boolean indexNameInvalid(String index) {
        return !indexNameValid(index);
    }

    @Override
    public Boolean indexExists(String index) {
        Assert.notNull(index, "索引名称为空");

        IndicesExistsRequest indicesExistsRequest = new IndicesExistsRequest(index);
        IndicesExistsResponse inExistsResponse = indicesAdminClient.exists(indicesExistsRequest).actionGet();
        return inExistsResponse.isExists();
    }
}
